/**
 * 
 */
package ipc;

import graph.Graph;

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.net.Socket;
import java.net.UnknownHostException;

import rddl.RDDL.KronDelta;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.locks.Lock;

import org.omg.CORBA._PolicyStub;

import dd.discrete.ADD;
import dd.discrete.ADDDNode;
import dd.discrete.ADDINode;
import dd.discrete.ADDNode;
import dd.discrete.DD;

import rddl.EvalException;
import rddl.RDDL;
import rddl.RDDL.AGG_EXPR;
import rddl.RDDL.BOOL_CONST_EXPR;
import rddl.RDDL.BOOL_EXPR;
import rddl.RDDL.Bernoulli;
import rddl.RDDL.COMP_EXPR;
import rddl.RDDL.CPF_DEF;
import rddl.RDDL.DOMAIN;
import rddl.RDDL.DiracDelta;
import rddl.RDDL.EXPR;
import rddl.RDDL.IF_EXPR;
import rddl.RDDL.INSTANCE;
import rddl.RDDL.INT_CONST_EXPR;
import rddl.RDDL.LCONST;
import rddl.RDDL.LTERM;
import rddl.RDDL.LTYPED_VAR;
import rddl.RDDL.LVAR;
import rddl.RDDL.NONFLUENTS;
import rddl.RDDL.OPER_EXPR;
import rddl.RDDL.PVARIABLE_DEF;
import rddl.RDDL.PVAR_EXPR;
import rddl.RDDL.PVAR_INST_DEF;
import rddl.RDDL.PVAR_NAME;
import rddl.RDDL.QUANT_EXPR;
import rddl.RDDL.TYPE_NAME;
import rddl.State;
import rddl.competition.Client;
import rddl.parser.ParseException;
import rddl.parser.parser;
import rddl.policy.Policy;
import rddl.viz.ElevatorDisplay;
import rddl.viz.RDDL2Graph;
import util.Pair;

import rddl.RDDL.REAL_CONST_EXPR;
/**
 * @author NR
 *
 * TODO
 */
public class MaxDecomposition implements Runnable{

	public State      _state;
	public INSTANCE   _i;
	public NONFLUENTS _n;
	public DOMAIN     _d;
	
	public static double EPSILON = 0.1;
	
	public static final int ORDER_XAXP = 1;
	public static final int ORDER_AXXP = 2;
	public static final int ORDER_XPAX = 3;
	public static final int ORDER_XXPA = 4;
	public static final int ORDER_AXPX = 5;
	public static final int ORDER_XPXA = 6;
	public static final int ORDER_GUESS = 7;
	private static final int ORDER_INTERLEAVE = 8;	
	
	private int _order;
	private ArrayList<String> _alOrder;
	
	public ADD         _context;
	public ArrayList<Integer> _alSaveNodes;
	public int         DD_ZERO;
	public int         DD_ONE;

	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmStateVars;
	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmNextStateVars;
	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmActionMap;
	public TreeMap<String, Pair<PVAR_NAME, ArrayList<LCONST> > > _hmObservVars;
	public HashMap<String,String> _hmPrimeRemap;
	
	public TreeMap<String, Integer> _var2transDD;
	public TreeMap<String, Integer> _var2observDD;
	
	public final static int     STATE_ITER  = 0;
	public final static int     OBSERV_ITER = 1;
	
	private int _rewardDD;
	private int _valueDD;
//	private Set<Integer> _hsConstraints = new HashSet<Integer>();
	
	private Map<String, ArrayList<String> > _hmact2vars = new HashMap<String, ArrayList<String> >();
	private Policy policy;
	
	public List<iterationData> iData = new ArrayList<iterationData>(); // :-)
	public preSolve presolve;
	
	public double avgR;
	public double varR;
	
	public long timetoplay;
	public postSolve postsolve;
//	private int conczeroonedd;
//	private Set<Integer> _hsZeroOneConstraints = new HashSet<Integer>();
	
	//pair of constraints
	//(zero,neginf)
	public runInfo rInfo;
	
	private TreeSet<Pair<Integer, Integer>> _hsPairConstraints = new TreeSet<Pair<Integer,Integer>>();
	private ArrayList<String> _alactionvars = new ArrayList<String>();
	private ArrayList<String> _alstatevars = new ArrayList<String>();
	private ArrayList<String> _alnextstatevars = new ArrayList<String>();
	private ArrayList<String> _alObservvars = new ArrayList<String>();
	private ArrayList<Pair<Integer, TreeMap<String, Boolean>>>	_alSavedds = new ArrayList<Pair<Integer,TreeMap<String,Boolean>>>();
	private HashMap<String, ArrayList<String> > _hmvars2act = new HashMap<String, ArrayList<String>>();
	public static final double BIGADD = 1e9;
	private static final boolean	DEBUG0	= false;
	private static final boolean	DEBUG2	= false;
	private static final boolean	DEBUG4	= false;
	private static final Runtime	RUNTIME	=  Runtime.getRuntime() ;
	private boolean ddready = false;
	private RDDL	_rddl;
	private int	_ndegree;
	private String	_sDestDir;
	
	private ArrayBlockingQueue<Pair<ADD,Integer>> _bq;
	
	public MaxDecomposition( final String domain_file, final String instance_file, 
									 final Double epsilon, final Integer order, final Integer degree,
									 final String dest_dir,
									 ArrayBlockingQueue<Pair<ADD,Integer>> bq) {
			
			try
			{
				_order = order;
				EPSILON = epsilon;
				
				RDDL rddl = new RDDL();
				
				File domf = new File(domain_file);
				File instf = new File(instance_file);
				
				
				rddl.addOtherRDDL(parser.parse(domf));
				rddl.addOtherRDDL(parser.parse(instf));
				
				_rddl = rddl;
				_ndegree = degree;
				_sDestDir = dest_dir;
				_bq = bq;
//				System.out.println(rInfo);
//				System.out.println(presolve);
				
			}catch(Exception e){
				e.printStackTrace();
			}
	}
	
	public MaxDecomposition(RDDL rddl, final double epsilon, final int order, final int degree,
			 final String dest_dir, ArrayBlockingQueue<Pair<ADD, Integer>> bq) throws Exception{
		_order = order;
		EPSILON = epsilon;
//		System.out.println(rInfo);
//		System.out.println(presolve);
		_rddl = rddl;
		_ndegree = degree;
		_sDestDir = dest_dir;
		_bq = bq;
	}
	
	public Pair<ArrayList<String>,ArrayList<Pair<Integer,String>>> setupVI() throws Exception{
		RDDL _rddltemp = _rddl;
		int degree = _ndegree;
		String dest_dir = _sDestDir;
//		System.out.println(rInfo);
//		System.out.println(presolve);

		
		this._d= _rddltemp._tmDomainNodes.entrySet().iterator().next().getValue();
		this._i= _rddltemp._tmInstanceNodes.entrySet().iterator().next().getValue();
		this._n= _rddltemp._tmNonFluentNodes.entrySet().iterator().next().getValue();
		
		if( degree != -1){
			_i._nNonDefActions = degree;
		}

		rInfo = new runInfo("ADD-CONC", _i._sName, _i._nNonDefActions, _d._sDomainName);
		
		_state = new State();
		
		_state.init(_n != null ? _n._hmObjects : null, _i._hmObjects,  
				_d._hmTypes, _d._hmPVariables, _d._hmCPF,
				_i._alInitState, _n == null ? null : _n._alNonFluents, 
				_d._alStateConstraints, _d._exprReward, _i._nNonDefActions);
		
		//for each ground state fluent
		//	get corresponding cpf EXPR
		//	ground it/ eliminate non fluents/ quantifiers
		//	1uild ADD
		//	show graph
		long _time = System.currentTimeMillis();
		try
		{
			buildCPTs();
		} catch (Exception e)
		{
			e.printStackTrace();
		}
		presolve = new preSolve( getSizeofR(), getSizeofP(), getSizeofA(), (System.currentTimeMillis()-_time)/(1000d*60));
//		_context.getGraph(sum).launchViewer();
		
		System.out.println(rInfo);
		System.out.println(presolve);
		ArrayList<Pair<Integer, String>> _alElimOrder = new ArrayList<Pair<Integer, String>>();
		ArrayList<String> _alSumOrder = new ArrayList<String>();
		makeOrders(_alSumOrder, _alElimOrder);
		
		return new Pair<ArrayList<String>, ArrayList<Pair<Integer,String>>>(_alSumOrder, _alElimOrder); 
	}
	
	public void run() {
				
			try {
				Pair<ArrayList<String>, ArrayList<Pair<Integer, String>>> ret = setupVI();
				ArrayList<String> _alSumOrder = ret._o1;
				ArrayList<Pair<Integer, String>> _alElimOrder = ret._o2;
				
				//value iteration begin
				_valueDD = DD_ZERO;
				
				int Vprev;
				
				boolean done = false;
				int iter = 0;
				
				
				long timetosolve = 0;
				
				while( !done ) {
					
					while ( _bq != null && _bq.remainingCapacity() == 0 ) {
						Thread.sleep(10000);
					}

					long itertime = System.currentTimeMillis();
					
					Vprev = _valueDD;

					try
					{
						_valueDD = computeNextV(_valueDD, Vprev, _alSumOrder, _alElimOrder, _alSavedds);
					} catch (Exception e)
					{
						e.printStackTrace();
					}
					
					flushCaches(Vprev);
					
					int diff  = _context.applyInt(_valueDD, Vprev, DD.ARITH_MINUS);
//					_context.setMinMax(diff);
					double max_pos_diff = Math.abs(_context.getMaxValue(diff));
                    double max_neg_diff = Math.abs(_context.getMinValue(diff));
                    if( max_neg_diff > 1e5 ){
                    	max_neg_diff = 0;
                    }
                    
                    double error = Math.max(max_pos_diff, max_neg_diff);

					++iter;
					System.out.println("iteration " + iter + " BE = " + error);
//					
//					_context.getGraph(_valueDD).launchViewer();
//					System.in.read();
//					System.out.println(_context.countExactNodes(_valueDD));
					
					if( iter == _i._nHorizon || error <= EPSILON ){
						done = true;
						
//						sizeofq = _context.countExactNodes(savedd);
//						_valueDD = savedd;
					}
					
					
					if( done ){
						flushCaches();
					}else{
						_alSavedds.clear();
						flushCaches();
					}

					itertime = (System.currentTimeMillis()-itertime);
					timetosolve += itertime;
					iData.add( new iterationData(iter, getSizeofV(), 0, 
							itertime /(1000d*60), error, 0 ) );
					
					
					
				}
				
				if( _bq != null ) {
					System.out.println("putting");
					_bq.put(new Pair<ADD, Integer>(_context, _valueDD));
					System.out.println("putted");
				}
				
//				makeDecisionDD(_alSumOrder);
				playDD();
				
				postsolve = new postSolve(avgR, varR, getSizeofV(), 0, timetoplay/(1000d*60), timetosolve/(1000d*60));
				
				
			}catch(Exception e) {
				e.printStackTrace();
			}
	}	
	
	private void makeDecisionDD(ArrayList<String> sumOrder) {
		
		int Vprimed = _context.remapGIDsInt(_valueDD, _hmPrimeRemap);
		if( !_context.verifyOrder(Vprimed) ){
			Vprimed = _context.align(Vprimed);
		}
		
		if(!_context.verifyOrder(Vprimed)) {
			System.err.println("Vprimed violates order");
			System.exit(1);
		}
		
		Vprimed = applyZeroOneConstraints(Vprimed);
		
		Set gids = _context.getGIDs(Vprimed);
		
		for( int i = 0 ; i < sumOrder.size(); ++i ){
			String primedvar = sumOrder.get(i);
			
			int primedvarid = (Integer)(_context._hmVarName2ID.get(primedvar));						
			
			if( ! gids.contains(primedvarid) ){
				if( DEBUG2 ) {
					System.out.println("Skipped " + primedvar);	
				}
				continue;
			}
			
			int dualdd = _var2transDD.get(primedvar);
//			dualdd = restrict(dualdd, assigns, Vcurr, Vprev);
			
			if( DEBUG2 ) {
				System.out.println("Multiplying " + primedvar);
			}
			
			Vprimed = _context.applyInt(dualdd, Vprimed , DD.ARITH_PROD);
			
			if( DEBUG2 ) {
				System.out.println("Summing " + primedvar);
			}
			
			Vprimed = _context.opOut(Vprimed , primedvarid, DD.ARITH_SUM);
			
			if( DEBUG2 ) {
				System.out.println("Expectation " + primedvar);
				System.out.println(" size: " + _context.countExactNodes(Vprimed));
				System.out.println(showMemory());
			}

			flushCaches(Vprimed);	
		}
		
		Vprimed = _context.scalarMultiply(Vprimed , _i._dDiscount);
		
		if( DEBUG2 ) {
			System.out.println("Size before making negative infs. to EV'");
			System.out.println(_context.countExactNodes(Vprimed ));
		}
		
//		Vprimed = applyConstraintsInterleaved(Vprimed);
		
//		Vprimed = restrict(Vprimed, assigns, Vprev); 
		
		if( DEBUG2 ) {
			System.out.println("Size after making negative infs. to EV'");
			System.out.println(_context.countExactNodes(Vprimed ));
		}
		
		if( DEBUG2 ) {
			System.out.println("Adding reward");
		}
		
		_alSavedds.clear();
		_alSavedds.add(new Pair<Integer, TreeMap<String,Boolean>>(_context.applyInt(Vprimed , _rewardDD, DD.ARITH_SUM), new TreeMap<String,Boolean>()));
		
	}

	/**
	 * @param saves 
	 * @param pVprimed
	 * @param pVprev
	 * @return
	 * @throws Exception 
	 */
	private int computeNextV(int Vcurr, int Vprev, ArrayList<String> sumOrder, ArrayList<Pair<Integer, String>> elimOrder, ArrayList<Pair<Integer,TreeMap<String, Boolean>>> saves) throws Exception {
		
		int Vprimed = _context.remapGIDsInt(Vcurr, _hmPrimeRemap);
		
		if( !_context.verifyOrder(Vprimed) ){
			if( DEBUG0 ){
				System.out.println("aligning");
//				System.out.println(_context.countExactNodes(Vprimed));
//				_context.getGraph(Vprimed).launchViewer();
			}
			Vprimed = _context.align(Vprimed);
//			System.out.println(_context.countExactNodes(Vprimed));
//			_context.getGraph(Vprimed).launchViewer();
//			System.in.read();
			flushCaches(Vprimed, Vprev);
		}
		
		
		
		if(!_context.verifyOrder(Vprimed)) {
			System.err.println("Vprimed violates order");
			System.exit(1);
		}
		
		if( DEBUG4 ) {
			System.out.println("Displaying V'");
			_context.getGraph(Vprimed).launchViewer();
			System.out.println("Press key");
			System.in.read();
		}
		
		//SANITY Check : Vprimed does not contain unprimed variables
		if( DEBUG0 ) {
			Set nodes = _context.getGIDs(Vprimed);
			for( String s : _alstatevars ) {
				if( nodes.contains(s) ) {
					System.err.println("V' contains x");
					System.exit(1);
				}
			}
			for( String a : _alactionvars ) {
				if( nodes.contains(a) ) {
					System.err.println("V' contains a");
					System.exit(1);
				}
			}
		}
		
		if( DEBUG2 ) {
			System.out.println("size of V' before zero constraint : " + _context.countExactNodes(Vprimed));	
		}
		
		Vprimed = applyNegInfConstraints(Vprimed, Vprev);
		
		if( DEBUG2 ) {
			System.out.println("size of V' after constraint : " + _context.countExactNodes(Vprimed));	
		}
		
		if( DEBUG4 ) {
			System.out.println("size of V' after constraint : " + _context.countExactNodes(Vprimed));
			System.out.println("Displaying V' after zero constraints");
			_context.getGraph(Vprimed).launchViewer();
			System.out.println("Press key");
			System.in.read();
		}

		TreeMap<String, Boolean> assigns = new TreeMap<String,Boolean>();
		
		_valueDD = computeExpectationHelper(Vprimed, Vprev, sumOrder, elimOrder, 0, 0,
				saves, assigns);
		
		Set gids = _context.getGIDs(_valueDD);
		for( String a : _alactionvars ){
			if( gids.contains(a) ){
				System.err.println("V has a");
				System.exit(1);
			}
		}
		
//		_context.PRUNE_TYPE = DD.REPLACE_AVG;
//		_context.PRUNE_PRECISION = 1e-2;
//		_valueDD = _context.pruneNodes(_valueDD);
//		System.out.println(_context.countExactNodes(_valueDD));

		return _valueDD;
		
	}

	/**
	 * @param pVprimed
	 * @param pVprev
	 * @param pSumOrder
	 * @param pElimOrder
	 * @param pI
	 * @return
	 * @throws Exception 
	 */
	private int computeExpectationHelper(int Vcurr, int Vprev,
			ArrayList<String> sumOrder, 
			ArrayList<Pair<Integer, String>> elimOrder, int sumIndex,
			int elimIndex,
			ArrayList<Pair<Integer,TreeMap<String,Boolean>>> saves, TreeMap<String, Boolean> assigns) throws Exception {
		
		if( sumIndex == sumOrder.size() ) {
			
			if( DEBUG0 ) {
				Set nodes = _context.getGIDs(Vcurr);
				for( String ns : _alnextstatevars ) {
					if( nodes.contains(ns) ) {
						System.err.println("EV' contains x' " + ns);
						System.err.println("Displaying V'");
						_context.getGraph(Vcurr).launchViewer();
						System.out.println("Press key");
						System.in.read();
						System.exit(1);
					}
				}
				for( String assign : assigns.keySet() ) {
					if( nodes.contains(assign) ) {
						System.err.println("EV' contains restricted " + assign);
						System.err.println("Displaying V'");
						_context.getGraph(Vcurr).launchViewer();
						System.out.println("Press key");
						System.in.read();
						System.exit(1);
					}
				}
			}
			
			//discount Vprimed
			Vcurr = _context.scalarMultiply(Vcurr, _i._dDiscount);
			
			if( DEBUG2 ) {
				System.out.println("Size before making negative infs. to EV'");
				System.out.println(_context.countExactNodes(Vcurr));
			}
			
//			Vcurr = applyConstraintsInterleaved(Vcurr, Vprev);
			
			Vcurr = restrict(Vcurr, assigns, Vprev); 
			
			if( DEBUG2 ) {
				System.out.println("Size after making negative infs. to EV'");
				System.out.println(_context.countExactNodes(Vcurr));
			}
			
			if( DEBUG2 ) {
				System.out.println("Adding reward");
			}
			
			
			Vcurr = _context.applyInt(Vcurr, restrict(_rewardDD, assigns, Vprev), DD.ARITH_SUM);
			
			if( DEBUG2 ) {
				System.out.println("Size of V before constraints " + _context.countExactNodes(Vcurr));
				System.out.println(showMemory());
			}
			
//			Vcurr = applyConstraintsInterleaved(Vcurr, Vprev);
			
//			restrict(Vcurr, assigns, Vprev);
			
			if( DEBUG2 ) {
				System.out.println("Size of V after constraints " + _context.countExactNodes(Vcurr));
				System.out.println(showMemory());
			}
			
			if( DEBUG2 ) {
				System.out.println("maxing out actions");
			}
			
			saves.add(new Pair<Integer,TreeMap<String,Boolean>>(Vcurr, new TreeMap<String, Boolean>(assigns)));
			
			if( DEBUG0 ) {
				System.out.println("Leaf node : " + assigns);
			}
			
//			Vcurr = maxOutActionVars(Vcurr, elimIndex, elimOrder, Vprev);
				
			if( DEBUG2 ) {
				System.out.println("Size after maxing actions " + _context.countExactNodes(Vcurr));
				System.out.println(showMemory());
			}

			if( DEBUG4 ) {
				System.out.println("Displaying V");
				_context.getGraph(_valueDD).launchViewer();
				System.out.println("Press key");
				System.in.read();
			}

			return Vcurr;

		}else {
			
//			Vcurr = applyZeroOneConstraints(, save)
			
			int i;
			
			Set gids = _context.getGIDs(Vcurr);
			
			for( i = sumIndex; i < sumOrder.size(); ++i ) {
			
				if( elimIndex < elimOrder.size() ){
					Pair<Integer, String> topElim = elimOrder.get(elimIndex);
					while( topElim._o1 == i ){
						if( DEBUG0 ){
							System.out.println("Maxing " + topElim);
						}
						Vcurr = _context.opOut(Vcurr, (Integer)(_context._hmVarName2ID.get(topElim._o2)),
								DD.ARITH_MAX);
						if( DEBUG2 ){
							System.out.println(_context.countExactNodes(Vcurr));
						}
						
						++elimIndex;
						if( elimIndex == elimOrder.size() ){
							break;
						}
						topElim = elimOrder.get(elimIndex);
					}
				}
				
//				long size = _context.countExactNodes(Vcurr);
//				System.out.println(size);
//				
//				if( size >= BIGADD ) {
//					
//					String var = pickVariable(Vcurr, Vprev);
//					
//					if( DEBUG0 ) {
//						System.out.println("Spliting at " + var);
//					}
//					
//					int id = (Integer)_context._hmVarName2ID.get(var);
//					
//					int ddt = _context.restrict(Vcurr, id , DD.RESTRICT_HIGH);
//					assigns.put(var, true);
//					
//					int ret_true = computeExpectationHelper(ddt, Vprev, sumOrder, elimOrder, i, saves, assigns);
//					
//					int ddf = _context.restrict(Vcurr, id, DD.RESTRICT_LOW);
//					assigns.put(var, false);
//					int ret_false = computeExpectationHelper(ddf, Vprev, sumOrder, elimOrder, i, saves, assigns);
//					
//					//unset
//					assigns.remove(var);
//					
//					return _context.applyInt(ddt, ddf, DD.ARITH_MAX);
//					
//				}else {
					String primedvar = sumOrder.get(i);
					
					int primedvarid = (Integer)(_context._hmVarName2ID.get(primedvar));						
					
					if( ! gids.contains(primedvarid) ){
						if( DEBUG2 ) {
							System.out.println("Skipped " + primedvar);	
						}
						continue;
					}
					
					int dualdd = _var2transDD.get(primedvar);
					dualdd = restrict(dualdd, assigns, Vcurr, Vprev);
					
					if( DEBUG2 ) {
						System.out.println("Multiplying " + primedvar);
					}
					
					Vcurr = _context.applyInt(dualdd, Vcurr, DD.ARITH_PROD);
					
					if( DEBUG2 ) {
						System.out.println("Summing " + primedvar);
					}
					Vcurr = _context.opOut(Vcurr, primedvarid, DD.ARITH_SUM);
					
					if( DEBUG2 ) {
						System.out.println("Expectation " + primedvar);
						System.out.println(" size: " + _context.countExactNodes(Vcurr));
						System.out.println(showMemory());
					}

					flushCaches(Vcurr, Vprev);	
//				}
				
//				while(size >= BIGADD){
//					System.out.println("Size before prune : " + size + " " + precision );
//					_context.PRUNE_TYPE = DD.REPLACE_AVG;
//					_context.PRUNE_PRECISION = precision;
//					Vprimed = _context.pruneNodes(Vprimed);
//					size = _context.countExactNodes(Vprimed);
//					System.out.println("Size after prune : " + size);
//					precision *= 1.2;
//					flushCaches(Vprimed, Vprev);
	//
//				}
	//
			}
			
//			if( !_context.verifyOrder(Vcurr) ){
//				System.err.println("EV' violates order");
//				System.exit(1);
//			}
			
			Vcurr = maxOutActionVars(Vcurr, elimIndex, elimOrder, Vcurr, Vprev );
			
			return computeExpectationHelper(Vcurr, Vprev, sumOrder, elimOrder, i, elimIndex, saves, assigns);
		
		}
	}

	/**
	 * @param pVcurr
	 * @param pVprev
	 * @return
	 */
	private String pickVariable(int Vcurr, int Vprev) {
//		_context.getGraph(Vcurr).launchViewer();
//		System.out.println(_context.printNode(Vcurr));
		
//		Set nodes = _context.getExactNodes(Vcurr);
//		
//		for( Object i : nodes ){
//			System.out.println(_context._hmID2VarName.get(i));
//		}
//		
		for( String a : _alactionvars ) {
			System.out.println(a);
			
			int id = (Integer)(_context._hmVarName2ID.get(a));
			
			
//			System.out.println(nodes.contains(id));
			
			long before = _context.countExactNodes(Vcurr);
			
			long aftert = _context.countExactNodes(_context.restrict(Vcurr, id, DD.RESTRICT_HIGH));
			long afterf = _context.countExactNodes(_context.restrict(Vcurr, id, DD.RESTRICT_LOW));
			System.out.println(before + " " + aftert + " " + afterf + " " + 0.5*(aftert+afterf) );
			
		}
		System.exit(1);
		return null;
	}

	/**
	 * @param pRewardDD
	 * @param pAssigns
	 * @param pVprev
	 * @return
	 */
	private int restrict(int dd, TreeMap<String, Boolean> assigns,
			int... save) {
		int ret = dd;
		int[] saved = new int[save.length+1];
		
		for( Map.Entry<String, Boolean> entry : assigns.entrySet() ) {
			int id = (Integer)_context._hmVarName2ID.get(entry.getKey());
			int op = entry.getValue() ? DD.RESTRICT_HIGH : DD.RESTRICT_LOW;
			ret = _context.restrict(ret, id, op);
			saved[save.length] = ret;
			flushCaches(saved);
		}
		
		return ret;
	}

	public String showMemory() {
		return ((RUNTIME.totalMemory()-RUNTIME.freeMemory())/1e6d) + " / " + (RUNTIME.totalMemory()/1e6d);
	}

	private double extent(int dd) {
		
		ADDNode root = _context.getNode(dd);
		if( root instanceof ADDDNode ){
			return 0;
		}else{
			ADDINode node = (ADDINode)root; 
			return node._dMaxLower-node._dMinLower;
		}
		
	}

	public void makeOrders(ArrayList<String> sumOrder, ArrayList<Pair<Integer, String>> elimOrder){	
		sumOrder.clear();
		elimOrder.clear();

		//go in order order of x'(sorted by naction vars)
		//add the action(s) affecting x' to list
		//pick next x' which has all its affecting actions in the list
		// i.e. score the list on how many x' (yet to come)
		// max out zero scored actions
		// next x' is one with smallest non zero score
		//if none carry on order
		
		System.out.println(_hmact2vars);
		System.out.println(_hmvars2act);
		
		Set<String> nextstatevars = _hmNextStateVars.keySet();
		Set<String> actionvars = _hmActionMap.keySet();
		
//		for( String var : _alOrder ){
//			if( actionvars.contains(var) ){
//				elimOrder.add(var);
//			}
//		}
		
		int i = 0;
		ArrayList<Pair<Integer,String>> scores = new ArrayList<Pair<Integer,String>>();
		for( Map.Entry<String, ArrayList<String>> entry : _hmact2vars.entrySet() ){
			scores.add(new Pair(entry.getValue().size(), entry.getKey()));
		}
		Collections.sort(scores);
		
		System.out.println(scores);
		
//		ArrayList<Pair<Integer, String>> scores = new ArrayList<Pair<Integer, String>>();
		
		while( sumOrder.size() != nextstatevars.size() ){
			ArrayList<String> nextxp = new ArrayList<String>();
			
			if( scores.isEmpty() ){
				while( !nextstatevars.contains(_alOrder.get(i) ) ){
					++i;	
				}
				if( !sumOrder.contains(_alOrder.get(i)) ){
					nextxp.add(_alOrder.get(i));
				}
				++i;
			}else{
				Pair<Integer, String> head = scores.get(0);
				scores.remove(head);
				for( String ele : _hmact2vars.get(head._o2) ){
					if( !sumOrder.contains(ele) ){
						nextxp.add(ele);
					}
				}
				
				elimOrder.add(new Pair<Integer, String>(sumOrder.size()+nextxp.size(), head._o2));
			}
			
			sumOrder.addAll(nextxp);
			
			//update scores
			for( String xp : nextxp ){
				ArrayList<String> acts = _hmvars2act.get(xp);
				
				for( int j = 0 ; j < scores.size(); ++j ){
					Pair<Integer, String> score = scores.get(j);
					if( acts.contains(score._o2) ){
						--score._o1;
					}
				}
				
			}
			Collections.sort(scores);
		}
		
//		Collections.reverse(elimOrder);
//		Collections.reverse(sumOrder);
		
		if( DEBUG0 ){
			System.out.println("Expectation order: " + sumOrder);
			System.out.println("Max order: " + elimOrder);
		}
	}

	
	private long getSizeofV() {
		return _context.countExactNodes(_valueDD);
	}

	private long getSizeofR() {
		return _context.countExactNodes(_rewardDD);
	}

	private long getSizeofA() {
		return _hmActionMap.size();
	}

	private long getSizeofP() {
		long ret = 0;
		
		for( Integer i : _var2transDD.values() ){
			ret += _context.countExactNodes(i);
		}
		
		return ret;
	}

	private void playDD() {
		
		policy = new Policy() {
		
			public double avg_reward = 0 ;
			public double variance = 0;//sum of squares till done
			public int horizon;
			public int numRounds = 30;
			private long ptime;
			ElevatorDisplay disp = new ElevatorDisplay();
			
			@Override
			public void roundEnd(double reward) {
//				super.roundEnd(reward);
				avg_reward += reward;
				variance += (reward*reward);
			}
			
			@Override
			public void sessionEnd(double total_reward) {
//				super.sessionEnd(total_reward);
				avg_reward /= numRounds;
				variance -= numRounds*(avg_reward*avg_reward);
				variance /= (numRounds-1);
				variance = Math.sqrt(variance);
//				System.out.println("Average reqward : " + avg_reward);
//				System.out.println("Stdev: " + variance);
				avgR = avg_reward;
				varR = variance;
				timetoplay = System.currentTimeMillis()-ptime;
			}
			
			@Override
			public void roundInit(double time_left, int horizon, int round_number, int total_rounds) {
//				super.roundInit(time_left, horizon, round_number, total_rounds);
				this.horizon = horizon;
				this.ptime = System.currentTimeMillis();
			}
			
			
			
			@Override
			public ArrayList<PVAR_INST_DEF> getActions(State s) throws EvalException {
		
				
		//		disp.getStateDescription(s);
				
				ArrayList<PVAR_INST_DEF> ret = new ArrayList<PVAR_INST_DEF>();
				
//				_context.getGraph(_valueDD).launchViewer();
//				
//				try {
//					Thread.sleep(100000);
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
				
				int maxdd = -1;
				double maxval = Double.NEGATIVE_INFINITY;
				int maxind = -1;
				
				for( int i = 0; i < _alSavedds.size(); ++i ) {
					
					int thissimplifieddd = _alSavedds.get(i)._o1;
					
					for( Map.Entry<String, Pair<PVAR_NAME, ArrayList<LCONST>>> entry : _hmStateVars.entrySet() ){
						String name = entry.getKey();
						Pair<PVAR_NAME, ArrayList<LCONST>> statevar = entry.getValue();
						
						Boolean val = (Boolean)s.getPVariableAssign(statevar._o1, statevar._o2);
//						System.out.println("State :  " + name + " : " + val );
						
						int nodeid = (Integer)_context._hmVarName2ID.get(name);
						int op = val ? DD.RESTRICT_HIGH : DD.RESTRICT_LOW;
						
						thissimplifieddd = _context.restrict(thissimplifieddd, nodeid, op);
						
					}

//					_context.getGraph(thissimplifieddd).launchViewer();
					
					ADDNode node = _context.getNode(thissimplifieddd);

					if( node instanceof ADDDNode ) {
						ADDDNode leaf = (ADDDNode)node;
						if( leaf._dUpper > maxval ) {
							maxval = leaf._dUpper;
							maxdd = thissimplifieddd;
							maxind = i;
						}
					}else if( node instanceof ADDINode ) {
						ADDINode internal = (ADDINode)node;
						if( internal._dMaxUpper > maxval ) {
							maxval = internal._dMaxUpper;
							maxdd = thissimplifieddd;
							maxind = i;
						}
					}
					
				}
				
				if( maxdd == -1 ) {
					System.err.println("improper policy");
					System.exit(1);
				}
				
				Pair<Integer, TreeMap<String, Boolean>> opt = _alSavedds.get(maxind);
//				_context.getGraph(opt._o1).launchViewer();
//				try {
//					Thread.sleep(1000000);
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
				for( Map.Entry<String, Boolean> entry : opt._o2.entrySet() ) {
					
					Pair<PVAR_NAME, ArrayList<LCONST>> act = _hmActionMap.get(entry.getKey());
					Boolean defaultval = (Boolean)_state.getDefaultValue(act._o1);
					if( entry.getValue() != defaultval ) {
						ret.add(new PVAR_INST_DEF(act._o1.toString(), entry.getValue(),
								act._o2));
					}
					
				}
				
				int dd = maxdd;
				
				while( true ){
					ADDNode nod = _context.getNode(dd);
					if( nod instanceof ADDDNode ){
//						System.out.println("Hit leaf value : " + ((ADDDNode)nod)._dUpper);
						break;
					}else{
						ADDINode n = (ADDINode)nod;
						int testvarid = n._nTestVarID;
						String testvarname = (String)_context._hmID2VarName.get(testvarid);
//						Syste
						Pair<PVAR_NAME, ArrayList<LCONST>> act = _hmActionMap.get(testvarname);
						Boolean defaultval = (Boolean) s.getDefaultValue(act._o1);
						
						double max = n._dMaxUpper;
						
						ADDNode truthnode = _context.getNode(n._nHigh);
						
						ADDNode falsenode = _context.getNode(n._nLow);
						
						double maxtrue, maxfalse;
						
						if( truthnode instanceof ADDINode ){
							maxtrue = ((ADDINode)truthnode)._dMaxUpper;
						}else{
							maxtrue = ((ADDDNode)truthnode)._dUpper;
						}
						
						if( falsenode instanceof ADDINode ){
							maxfalse = ((ADDINode)falsenode)._dMaxUpper;
						}else{
							maxfalse = ((ADDDNode)falsenode)._dUpper;
						}
						
						if( max == maxfalse ){
							if( defaultval != false ){
								ret.add(new PVAR_INST_DEF(act._o1.toString(), false, act._o2));
							}
							dd = _context.restrict(dd, testvarid, DD.RESTRICT_LOW);
						}else if( max == maxtrue ){
							if( defaultval != true ){
								ret.add(new PVAR_INST_DEF(act._o1.toString(), true, act._o2));
							}
							dd = _context.restrict(dd, testvarid, DD.RESTRICT_HIGH);
						}
						
					}
				}
				
				flushCaches();
				
				return ret;
			}
		
			public double getAvg_reward() {
				return avg_reward;
			}
			
			public double getVariance() {
				return variance;
			}
			
		};
		
		
		Client cl = new Client(this._d, this._i, this._n, policy, "localhost", 2316, "BEAVER!!!");
		
		
	}
	
	private int applyConstraintsInterleaved(int dd, int... save){
		int ret = dd;
		Iterator<Pair<Integer, Integer>> it = _hsPairConstraints.iterator();
		
		int[] saved = new int[save.length+1];
		for( int i = 0 ; i < save.length; ++i ){
			saved[i] = save[i];
		}
		
		while( it.hasNext() ){
			Pair<Integer, Integer> con = it.next();
			ret = _context.applyInt(ret, con._o1,	DD.ARITH_PROD);
			ret = _context.applyInt(ret, con._o2, DD.ARITH_SUM);
			saved[save.length] = ret;
			flushCaches(saved);
		}
		
		return ret;
		
	}
	
	private int applyNegInfConstraints(int dd, int... save) {
		int ret = dd ; 
		
		int[] saved = new int[save.length+1];
		for( int i = 0 ; i < save.length; ++i ){
			saved[i] = save[i];
		}
		
		Iterator<Pair<Integer, Integer>> it = _hsPairConstraints.iterator();
		
		while( it.hasNext() ){
			ret = _context.applyInt(ret, it.next()._o2, DD.ARITH_SUM);
			saved[save.length] = ret;
			flushCaches(saved);
		}
		
		return ret;
		
	}

	private int applyZeroOneConstraints(int dd, int... save) {
		int ret = dd ; 
		
		int[] saved = new int[save.length+1];
		for( int i = 0 ; i < save.length; ++i ){
			saved[i] = save[i];
		}
		
		Iterator<Pair<Integer, Integer>> it = _hsPairConstraints.iterator();
		
		while( it.hasNext() ){
			ret = _context.applyInt(ret, it.next()._o1, DD.ARITH_PROD);
			saved[save.length] = ret;
			flushCaches(saved);
		}
		
		return ret;
		
	}

	private int maxOutActionVars(int dd, int elimIndex, ArrayList<Pair<Integer,String> > _alElimOrder, 
			int... save) {
		
		int ret = dd ;
		int[] saved = new int[save.length+1];
		for( int i = 0 ; i < save.length; ++i ){
			saved[i] = save[i];
		}
		
		for( int i = elimIndex; i < _alElimOrder.size(); ++i ){
			String act = _alElimOrder.get(i)._o2; 
			int actid = (Integer)_context._hmVarName2ID.get(act);
//			System.out.println(act);
			ret = _context.opOut(ret, actid, DD.ARITH_MAX);
			saved[saved.length-1] = ret;
			flushCaches(saved);
//			System.out.println(_context.countExactNodes(ret));
		}
		
		return ret;
		
	}

	public void flushCaches(int... save){
		_context.clearSpecialNodes();
		_context.addSpecialNode(_rewardDD);
		_context.addSpecialNode(_valueDD);
		for( int special : _alSaveNodes ){
			_context.addSpecialNode(special);
		}
		
		for( Pair<Integer,Integer> special : _hsPairConstraints ){
			_context.addSpecialNode(special._o1);
			_context.addSpecialNode(special._o2);
		}
		
		for( int special : save ){
			_context.addSpecialNode(special);
		}
		
		for( Pair<Integer, TreeMap<String, Boolean>> special : _alSavedds ) {
			_context.addSpecialNode(special._o1);	
		}
		
		_context.flushCaches(DEBUG2);
	}
	
	public void buildCPTs() throws Exception {

//		System.err.println("building cpts");
		
		_var2transDD = new TreeMap<String,Integer>();
		_var2observDD = new TreeMap<String,Integer>();
		
		if (_state._tmIntermNames.size() > 0)
			throw new Exception("Cannot convert to SPUDD format: contains intermediate variables");

		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> state_vars  = collectStateVars();
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> action_vars = collectActionVars();
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> observ_vars = collectObservationVars();
		
		
		_hmStateVars = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>();
		_hmPrimeRemap = new HashMap<String,String>();
		_hmNextStateVars = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>();
		
		for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : state_vars.entrySet()) {
			PVAR_NAME p = e.getKey();
			ArrayList<ArrayList<LCONST>> assignments = e.getValue();
			for (ArrayList<LCONST> assign : assignments) {
				String name = CleanFluentName(p.toString() + assign);
				_hmStateVars.put(name, new Pair(p, assign));
				_alstatevars.add(name);
				_hmNextStateVars.put(name + "'", new Pair(p, assign));
				_hmPrimeRemap.put(name, name + "'");
				_alnextstatevars.add(name+"'");
			}
		}
		
		_hmActionMap = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>();
		for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : action_vars.entrySet()) {
			PVAR_NAME p = e.getKey();
			ArrayList<ArrayList<LCONST>> assignments = e.getValue();
			for (ArrayList<LCONST> assign : assignments) {
				String name = CleanFluentName(p.toString() + assign);
				_hmActionMap.put(name, new Pair(p, assign));
				_alactionvars.add(name);
			}
		}

		_hmObservVars = new TreeMap<String, Pair<PVAR_NAME,ArrayList<LCONST>>>(); 
		
		for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : observ_vars.entrySet()) {
			PVAR_NAME p = e.getKey();
			ArrayList<ArrayList<LCONST>> assignments = e.getValue();
			for (ArrayList<LCONST> assign : assignments) {
				String name = CleanFluentName(p.toString() + assign);
				_hmObservVars.put(name + "'", new Pair(p, assign)); // SPUDD uses primed observation vars
				_alObservvars.add(name+"'");
			}
		}

		_alOrder = new ArrayList<String>();
		
		if( _order != ORDER_GUESS ){
			_alOrder = makeOrdering(state_vars, observ_vars, action_vars);
		}else{
			_alOrder = guessOrdering(state_vars, observ_vars, action_vars);
		}
		
		_context = new ADD(_alOrder);

		DD_ONE = _context.getConstantNode(1d);
		DD_ZERO = _context.getConstantNode(0d);
		_valueDD = DD_ZERO;
		_rewardDD = DD_ZERO;
		
		_alSaveNodes = new ArrayList<Integer>();
		_alSaveNodes.add(DD_ONE);
		_alSaveNodes.add(DD_ZERO);
		
		if( DEBUG4 ) {
			System.out.println("State vars:  " + state_vars);
			System.out.println("Action vars: " + action_vars);
			System.out.println("Observ vars: " + observ_vars);	
		}

		
		for (int iter = STATE_ITER; iter <= OBSERV_ITER; iter++) {
			
			TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> src = 
				iter == STATE_ITER ? state_vars : observ_vars;
			
			for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : src.entrySet()) {
				
				// Go through all variable names p for a variable type
				PVAR_NAME p = e.getKey();
				ArrayList<ArrayList<LCONST>> assignments = e.getValue();
				//System.out.println(_state._hmCPFs);
				
				CPF_DEF cpf = _state._hmCPFs.get(new PVAR_NAME(p.toString() + 
						(iter == STATE_ITER ? "'" : "")));
				
				
				HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();
				for (ArrayList<LCONST> assign : assignments) {
	
					String cpt_var = CleanFluentName(p.toString() + assign);
//					System.out.println("Processing: " + cpt_var);
	
					subs.clear();
					for (int i = 0; i < cpf._exprVarName._alTerms.size(); i++) {
						LVAR v = (LVAR)cpf._exprVarName._alTerms.get(i);
						LCONST c = (LCONST)assign.get(i);
						subs.put(v,c);
					}
					
					HashSet<Pair> relevant_vars = new HashSet<Pair>();
					EXPR cpf_expr = cpf._exprEquals;
					if (_d._bCPFDeterministic) // collectGFluents expects a distribution so convert to a Delta function if needed
						cpf_expr = new KronDelta(cpf_expr);					
					
					cpf_expr.collectGFluents(subs, _state, relevant_vars);

					add2InfluenceMap(cpt_var+"'", relevant_vars, action_vars);
					
//					for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e2 : action_vars.entrySet()) {
//	
//						PVAR_NAME action_name = e2.getKey();
//						ArrayList<ArrayList<LCONST>> action_assignments = e2.getValue();
//						Boolean defaultval = (Boolean)_state.getDefaultValue(action_name);
//						
//						for (ArrayList<LCONST> action_assign : action_assignments)
//							_state.setPVariableAssign(action_name, action_assign, defaultval);
//	
//					}

					int cpt = enumerateAssignments(new ArrayList<Pair>(relevant_vars), cpf_expr, subs, 0);
					
					//build dual diagram
					int unprime_id = (Integer)_context._hmVarName2ID.get(cpt_var);
					int prime_id = (Integer)_context._hmVarName2ID.get(cpt_var+"'");
						
					int prob_true = cpt;
						
					int prob_false = _context.applyInt(DD_ONE, prob_true, DD.ARITH_MINUS);
						
					int high_branch = _context.getVarNode(prime_id, 0.0d, 1.0d);
						
					int low_branch = _context.getVarNode(prime_id, 1.0d, 0.0d);
						
					int low = _context.applyInt(low_branch, prob_false, DD.ARITH_PROD);
						
					int high = _context.applyInt(high_branch, prob_true, DD.ARITH_PROD);
						
					cpt = _context.applyInt(high, low,DD.ARITH_SUM);
					
					_alSaveNodes.add(cpt);
					
					if (iter == STATE_ITER)
						_var2transDD.put(cpt_var+"'", cpt);
					else
						_var2observDD.put( cpt_var+"'", cpt);
					
					if( DEBUG4 ) {
						System.out.println("Displaying CPT for " + cpt_var+"'");
						_context.getGraph(cpt).launchViewer();
						System.out.println("Press any key");
						System.in.read();
					}
				}
			}
		}
		
		
		getConcurrencyDDFactored();
		
		
		for( EXPR constraint : _state._alConstraints ){
//			System.out.println("Constraint : " + constraint ); 
			if( constraint instanceof BOOL_EXPR ){
				BOOL_EXPR be = (BOOL_EXPR)constraint;
				
				addConstraint(be);
				
			}else{
				System.err.println("Constraint not tpye of bool expr");
				System.exit(1);
			}
		}
		
		EXPR rew_expr =  _state._reward;
		try {
			
			ArrayList<Integer> rew = convertAddExpr2ADD(rew_expr, false);
			_rewardDD = DD_ZERO;
			for( int subd : rew ){
				_rewardDD = _context.applyInt(_rewardDD, subd, DD.ARITH_SUM);
			}
			
			//get rmin
			double rmin = Math.abs(_context.getMin(_rewardDD));
//			System.out.println(" Adding = " + (1+rmin) );
//			//get constant node
			int shiftdd = _context.getConstantNode(1+rmin);
//			//add to reward
			_rewardDD = _context.applyInt(_rewardDD, shiftdd, DD.ARITH_SUM );
//			_rewardDD = applyConstraintsInterleaved(_rewardDD);
			_context.addSpecialNode(_rewardDD);
			
			if( DEBUG4 ) {
				System.out.println("Displaying reward");
				_context.getGraph(_rewardDD).launchViewer();
				System.out.println("Press key");
				System.in.read();
			}

			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	private void addConstraint(BOOL_EXPR be) throws Exception {
		HashMap<LVAR, LCONST> empty = new HashMap<LVAR, LCONST>();
		HashSet<Pair> gfluents = new HashSet<Pair>();
		be.collectGFluents(empty, _state, gfluents);
		
		IF_EXPR ife = new IF_EXPR(be, new KronDelta(new BOOL_CONST_EXPR(true)), 
				new KronDelta(new BOOL_CONST_EXPR(false)) );
		
		int zerodd = enumerateAssignments(new ArrayList<Pair>(gfluents), ife, empty	, 0 );
		
		empty.clear();
		
		ife = new IF_EXPR(be, new DiracDelta(new REAL_CONST_EXPR(0.0d)), 
				new DiracDelta(new REAL_CONST_EXPR(ADD.NEGINF)) );
		
		int neginfdd = enumerateAssignments(new ArrayList<Pair>(gfluents), ife, empty, 0);
		
		
		_hsPairConstraints.add(new Pair<Integer,Integer>(zerodd,neginfdd));
		
		if( DEBUG4 ) {
			System.out.println("Displaying constraint : " + be);
			_context.getGraph(zerodd).launchViewer();
			_context.getGraph(neginfdd).launchViewer();
			System.out.println("Press key...");
			System.in.read();
		}
		
		
	}
	
	private void add2InfluenceMap(String cpt_var, HashSet<Pair> relevant_vars, TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) {
		
		for( Pair pair : relevant_vars ){
			PVAR_NAME pvar = (PVAR_NAME)pair._o1;
			ArrayList<LCONST> term = (ArrayList<LCONST>)pair._o2;
			
//			System.out.println(pvar.toString() + " " + term + " " + (action_vars.get(pvar)!=null) );
			
			if( action_vars.get(pvar) != null ){
				
				String actname = CleanFluentName(pvar.toString() + term);
				
				if( _hmact2vars.get(actname) == null  ){
					_hmact2vars.put(actname, new ArrayList<String>());
				}
				
				_hmact2vars.get(actname).add(cpt_var);
				
				if( _hmvars2act .get(cpt_var) == null  ){
					_hmvars2act.put(cpt_var, new ArrayList<String>());
				}
				
				_hmvars2act.get(cpt_var).add(actname);
				
				
			}
			
		}

//		System.exit(1);
		
	}
	
	private ArrayList<Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>>>  getCptOrderSortedByActionVars(TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> state_vars, 
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> observ_vars, TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) throws EvalException{
		ArrayList<Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>>> cptOrder = 
			new ArrayList<Pair<Integer,Pair<PVAR_NAME, ArrayList<LCONST> >>>();
		
		for (int iter = STATE_ITER; iter <= OBSERV_ITER; iter++) {
			
			TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> src = 
				iter == STATE_ITER ? state_vars : observ_vars;
			
			for (Map.Entry<PVAR_NAME,ArrayList<ArrayList<LCONST>>> e : src.entrySet()) {
				
				PVAR_NAME p = e.getKey();
				ArrayList<ArrayList<LCONST>> assignments = e.getValue();
				
				CPF_DEF cpf = _state._hmCPFs.get(new PVAR_NAME(p.toString() + 
						(iter == STATE_ITER ? "'" : "")));
				
				HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();
				for (ArrayList<LCONST> assign : assignments) {
	
					String cpt_var = CleanFluentName(p.toString() + assign);
					System.out.println("Processing: " + cpt_var);
					
	
					subs.clear();
					for (int i = 0; i < cpf._exprVarName._alTerms.size(); i++) {
						LVAR v = (LVAR)cpf._exprVarName._alTerms.get(i);
						LCONST c = (LCONST)assign.get(i);
						subs.put(v,c);
					}
					
					EXPR cpf_expr = cpf._exprEquals;
					
					HashSet<Pair> relvars = new HashSet<Pair>();
					cpf_expr.collectGFluents(subs, _state, relvars);
					
					int count = 0;
					
					for( Pair pa : relvars ){
						PVAR_NAME pvar = (PVAR_NAME)pa._o1;
						ArrayList<LCONST> ass = (ArrayList<LCONST>)pa._o2;
						String name = CleanFluentName(pvar.toString()+ass);
						
						if( _state.getPVariableType(pvar) == _state.ACTION ){
							++count;
						}
					}
					
					cptOrder.add(new Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST> > >(count, new Pair<PVAR_NAME, ArrayList<LCONST> >(p, assign)));
				}
			}
		}
	
//		System.out.println(cptOrder);
		
		Comparator<Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>>> cptComparator = 
			new Comparator<Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>>>() {
		
			public int compare(Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>> o1,
					Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>> o2) {
				
				if( o1._o1.compareTo(o2._o1) != 0 ){
					return o1._o1.compareTo(o2._o1);
				}else{
					if( o1._o2._o1.compareTo(o2._o2._o1) != 0 ){
						return o1._o2._o1.compareTo(o2._o2._o1);
					}else{
						return 0;
					}
				}
				
			}
		};
		
		Collections.sort(cptOrder, cptComparator);
//		Collections.reverse(cptOrder);
		System.out.println(cptOrder);
		return cptOrder;
	}

private ArrayList<String> guessOrdering(TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> state_vars, 
		TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> observ_vars, TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) throws EvalException {
	
//	String[] ord = {
//			"person_waiting_down__f0", 
//			"person_waiting_down__f1",
//			"person_waiting_down__f2",
//			"person_waiting_up__f0", 
//			"person_waiting_up__f1", 
//			"person_waiting_up__f2",
//
//			"person_waiting_down__f0'", 
//			"person_waiting_down__f1'",
//			"person_waiting_down__f2'",
//			"person_waiting_up__f0'", 
//			"person_waiting_up__f1'", 
//			"person_waiting_up__f2'",
//			
//			"person_in_elevator_going_down__e0", 
//			"person_in_elevator_going_down__e0'",
//			
//			"person_in_elevator_going_up__e0", 
//			"person_in_elevator_going_up__e0'",
//			
//			"elevator_at_floor__e0_f0", 
//			"elevator_at_floor__e0_f1",
//			"elevator_at_floor__e0_f2",
//			
//			"elevator_at_floor__e0_f0'", 
//			"elevator_at_floor__e0_f1'", 
//			"elevator_at_floor__e0_f2'",
//			
//			"elevator_closed__e0",
//			"elevator_dir_up__e0",
//			
//			"elevator_closed__e0'",
//			"elevator_dir_up__e0'",
//			
//			"person_in_elevator_going_down__e1", 
//			"person_in_elevator_going_down__e1'",
//			
//			"person_in_elevator_going_up__e1", 
//			"person_in_elevator_going_up__e1'",
//			
//			"elevator_at_floor__e1_f0", 
//			"elevator_at_floor__e1_f1",
//			"elevator_at_floor__e1_f2",
//			
//			"elevator_at_floor__e1_f0'",
//			"elevator_at_floor__e1_f1'", 
//			"elevator_at_floor__e1_f2'",
//			
//			"elevator_closed__e1",
//			"elevator_dir_up__e1",
//			
//			"elevator_closed__e1'", 
//			"elevator_dir_up__e1'",
//			
//			"open_door_going_up__e0", 
//			"open_door_going_down__e0", 
//			"close_door__e0", 
//			"move_current_dir__e0",
//			
//			"open_door_going_up__e1", 
//			"open_door_going_down__e1", 
//			"close_door__e1", 
//			"move_current_dir__e1"
//			};
//	
//		ArrayList<String> ret = new ArrayList<String>();
//		for( String s : ord ){
//			ret.add(s);
//		}
//		return ret;
		
		ArrayList<Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>>> cptOrder = 
			getCptOrderSortedByActionVars(state_vars, observ_vars, action_vars);
		
		ArrayList<String> order = new ArrayList<String>();
		
//		while( cpt)
		
		for ( Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST> > > cpt : cptOrder ) {

			PVAR_NAME p = cpt._o2._o1;
			CPF_DEF cpf = _state._hmCPFs.get(new PVAR_NAME(p.toString()+ "'"));
			HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();
			ArrayList<LCONST> assign = cpt._o2._o2;
			String cpt_var = CleanFluentName(p.toString() + assign);
			subs.clear();
			for (int i = 0; i < cpf._exprVarName._alTerms.size(); i++) {
				LVAR v = (LVAR)cpf._exprVarName._alTerms.get(i);
				LCONST c = (LCONST)assign.get(i);
				subs.put(v,c);
			}
					
			EXPR cpf_expr = cpf._exprEquals;
					
			HashSet<Pair> relvars = new HashSet<Pair>();
			cpf_expr.collectGFluents(subs, _state, relvars);
					
			for( Pair pa : relvars ){
				PVAR_NAME pvar = (PVAR_NAME)pa._o1;
				ArrayList<LCONST> ass = (ArrayList<LCONST>)pa._o2;
				String name = CleanFluentName(pvar.toString()+ass);
				
				if( !order.contains(name) && _state.getPVariableType(pvar) == _state.STATE ){
					order.add(name);	
				}
						
			}
					
			for( Pair pa : relvars ){
				PVAR_NAME pvar = (PVAR_NAME)pa._o1;
				ArrayList<LCONST> ass = (ArrayList<LCONST>)pa._o2;
				String name = CleanFluentName(pvar.toString()+ass);
				
				if( !order.contains(name) && _state.getPVariableType(pvar) == _state.ACTION ){
					order.add(name);	
				}
				
			}
					
			order.add(cpt_var+"'");
					
		}
		
		System.out.println(order);
		
		if( order.size() != 2*_alstatevars.size() + _alactionvars.size() + _alObservvars.size() ) {
			System.err.println("size of ordering not equal to size of variables");
			System.out.println(order);
			System.out.println(_alstatevars);
			System.out.println(_alactionvars);
			System.out.println(_alObservvars);
			
			System.exit(1);
		}
		
		if( DEBUG0 ) {
			System.out.println("Problem has " + order.size() + " variables ");
			System.out.println("State vars : " + _alstatevars.size());
			System.out.println("Action vars : " + _alactionvars.size());
			System.out.println("Ordering : " + order);	
		}
		
		
		return order;
	}

	private ArrayList<String> makeOrdering(
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> state_vars, 
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> observ_vars, 
			TreeMap<PVAR_NAME, ArrayList<ArrayList<LCONST>>> action_vars) throws EvalException {
		
//		System.out.println(_hmStateVars.keySet());
//		System.out.println(_hmActionMap.keySet());
//		System.out.println(_hmNextStateVars.keySet());
//		
//		
		ArrayList<String> order = new ArrayList<String>();
//		
//		String[] ord = {"person_in_elevator_going_up__e0", "person_in_elevator_going_down__e0", 
//				"elevator_closed__e0", 
//				"elevator_at_floor__e0_f0", "elevator_dir_up__e0", "elevator_at_floor__e0_f1", "elevator_at_floor__e0_f2",
//				"person_waiting_up__f0", "person_waiting_up__f1", "person_waiting_up__f2", 
//				"person_waiting_down__f2", "person_waiting_down__f0", "person_waiting_down__f1"}; 
//		
//		return order;
		
		switch(_order){
		
			case ORDER_INTERLEAVE:
				
//				ArrayList<Pair<Integer, Pair<PVAR_NAME, ArrayList<LCONST>>>> sortedorder = 
//					getCptOrderSortedByActionVars(state_vars, observ_vars, action_vars);

//				for( )
				
				for( String s : _alstatevars ) {
					order.add(s);
					order.add(s+"'");
				}
				
				order.addAll(_alactionvars);
				
				break;
				
			case ORDER_AXPX:
				order.addAll(_alactionvars);
				order.addAll(_alnextstatevars);
				order.addAll(_alstatevars);
				
				break;
				
			case ORDER_AXXP:
				order.addAll(_alactionvars);
				order.addAll(_alstatevars);
				order.addAll(_alnextstatevars);
				break;
			
			case ORDER_XAXP:
				
				order.addAll(_alstatevars);
				order.addAll(_alactionvars);
				order.addAll(_alnextstatevars);
				break;
				
			case ORDER_XPAX:
				
				order.addAll(_alnextstatevars);
				order.addAll(_alactionvars);
				order.addAll(_alstatevars);
				break;
				
			case ORDER_XPXA:
				
				order.addAll(_alnextstatevars);
				order.addAll(_alstatevars);
				order.addAll(_alactionvars);
				break;
			
			default:
				System.err.println("Unknown ordering...");
			
			case ORDER_XXPA:

				
				order.addAll(_alstatevars);
				order.addAll(_alnextstatevars);
				order.addAll(_alactionvars);
		}
		
		if( DEBUG0 ) {
			System.out.println("Problem has " + order.size() + " variables");
			System.out.println("Ordering : " + order);
		}
		
		return order;
	}
	

	public TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> collectStateVars() 
	throws EvalException {
	
		TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> state_vars = 
			new TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>>();

		for (PVAR_NAME p : _state._alStateNames) {
			ArrayList<ArrayList<LCONST>> gfluents = _state.generateAtoms(p);
			state_vars.put(p, gfluents);
		}
	
		return state_vars;
}
	
	// Returns Pair(HashMap subs, EXPR e)
	public ArrayList<Pair> getAdditiveComponents(EXPR e) throws Exception {

		ArrayList<Pair> ret = new ArrayList<Pair>();
		
		if (e instanceof OPER_EXPR && ((OPER_EXPR)e)._op == OPER_EXPR.PLUS) {

			OPER_EXPR o = (OPER_EXPR)e;

			//System.out.println("\n- Oper Processing " + o._e1);
			//System.out.println("\n- Oper Processing " + o._e2);
			
			ret.addAll(getAdditiveComponents(o._e1));
			ret.addAll(getAdditiveComponents(o._e2));
						
		} else if (e instanceof AGG_EXPR && ((AGG_EXPR)e)._op == AGG_EXPR.SUM) {
			
			AGG_EXPR a = (AGG_EXPR)e;
			
			ArrayList<ArrayList<LCONST>> possible_subs = _state.generateAtoms(a._alVariables);
			HashMap<LVAR,LCONST> subs = new HashMap<LVAR,LCONST>();

			//System.out.println("\n- Sum Processing " + a);

			// Evaluate all possible substitutions
			for (ArrayList<LCONST> sub_inst : possible_subs) {
				for (int i = 0; i < a._alVariables.size(); i++) {
					subs.put(a._alVariables.get(i)._sVarName, sub_inst.get(i));
				}
				
				// Note: we are not currently decomposing additive structure below a sum aggregator
				ret.add(new Pair(subs.clone(), a._e));			
				
				subs.clear();
			}

		} else {
			//System.out.println("\n- General Processing " + e);
			HashMap<LVAR,LCONST> empty_subs = new HashMap<LVAR,LCONST>();
			ret.add(new Pair(empty_subs, e));
		}
		
		return ret;
	}
	
private void getConcurrencyDDFactored() throws Exception {
		
		int ret = DD_ZERO;
		
		
		//changed to simply add indicator diagrams for each action var
		//10/19/2011 -Aswin
		
		for( String action_var : _alactionvars ) {
			int dd = _context.getVarNode(action_var, 0.0d, 1.0d);
			ret = _context.applyInt(ret, dd, DD.ARITH_SUM);
		}
		
		ret = _context.AddtoBdd(ret, _i._nNonDefActions);
		
		int zeroonedd = _context.applyInt(DD_ONE, ret, DD.ARITH_MINUS);
		
		HashMap remap = new HashMap();
		
		ADDDNode dnode0 = (ADDDNode)_context.getNode(_context.getConstantNode(0.0d));
		ADDDNode dnode1 = (ADDDNode)_context.getNode(_context.getConstantNode(1.0d));
		ADDDNode dnodeneginf = (ADDDNode)_context.getNode(_context.getConstantNode(ADD.NEGINF));
		remap.put(dnode0, dnodeneginf);
		remap.put(dnode1, dnode0);
		
		int neginfzerodd = _context.reduceRemapLeaves(ret, remap);
		
		_hsPairConstraints.add(new Pair<Integer,Integer>(zeroonedd,neginfzerodd));
		
		if( DEBUG4 ) {
			System.out.println("Displaying concurrency constraint");
			_context.getGraph(zeroonedd).launchViewer();
			_context.getGraph(neginfzerodd).launchViewer();
			System.out.println("press key");
			System.in.read();
		}
	}

public ArrayList<Integer> convertAddExpr2ADD(EXPR e, boolean filter_actions) throws Exception {
		
		ArrayList<Integer> adds = new ArrayList<Integer>();
		ArrayList<Pair> exprs = getAdditiveComponents(e);
		
//		System.out.println("\n");
		for (Pair p : exprs) {
			String str = "";
			if (p._o2 instanceof RDDL.OPER_EXPR)
				str = ((RDDL.OPER_EXPR)p._o2)._op;
//			System.out.println("Found pair: " + p._o1 + " -- " + p._o2.getClass() + " / " + str + "\n" + p);
		}
		
		int ZERO_ADD = _context.getConstantNode(0d); 
		
		for (Pair p : exprs) {
			
			HashSet<Pair> relevant_vars = new HashSet<Pair>();
			HashMap subs = (HashMap)p._o1;
			EXPR e2 = ((EXPR)p._o2);
			if (_d._bRewardDeterministic) // collectGFluents expects distribution
				e2 = new DiracDelta(e2);
			else{
//				System.out.println("WARNING: May not convert additive reward correctly... check results.");
			}
				
			e2.collectGFluents(subs, _state, relevant_vars);
			
//				System.out.println("  - relevant vars: " + relevant_vars);
			
			int add = enumerateAssignments(new ArrayList<Pair>(relevant_vars), e2, subs, 0);
			if (add != ZERO_ADD)
				adds.add(add);
		}
//		System.out.println("Done processing additive expression");
		
		return adds;
	}

public TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> collectActionVars() 
	throws EvalException {
	
	TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> action_vars = 
		new TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>>();

	for (PVAR_NAME p : _state._alActionNames) {
		ArrayList<ArrayList<LCONST>> gfluents = _state.generateAtoms(p);
		action_vars.put(p, gfluents);
	}
	
	return action_vars;
}

public TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> collectObservationVars() 
	throws EvalException {

	TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>> observ_vars = 
		new TreeMap<PVAR_NAME,ArrayList<ArrayList<LCONST>>>();

	for (PVAR_NAME p : _state._alObservNames) {
		ArrayList<ArrayList<LCONST>> gfluents = _state.generateAtoms(p);
		observ_vars.put(p, gfluents);
	}
	
	return observ_vars;
}

public static String CleanFluentName(String s) {
	s = s.replace("[", "__");
	s = s.replace("]", "");
	s = s.replace(", ", "_");
	s = s.replace(',','_');
	s = s.replace(' ','_');
	s = s.replace('-','_');
	s = s.replace("()","");
	s = s.replace("(", "__");
	s = s.replace(")", "");
	if (s.endsWith("__"))
		s = s.substring(0, s.length() - 2);
	return s;
}

public int enumerateAssignments(ArrayList<Pair> vars, 
		EXPR cpf_expr, HashMap<LVAR,LCONST> subs, int index) 
	throws Exception {
	return enumerateAssignments(vars, cpf_expr, subs, index, _context.getConstantNode(1d));
}

public int enumerateAssignments(ArrayList<Pair> vars, 
		EXPR cpf_expr, HashMap<LVAR,LCONST> subs, int index, int cpt) 
	throws EvalException, Exception {

	// Need to build an assignment ADD on the way down, use it 
	// at leaf to add to current CPT and return result
	
	// Recurse until index == vars.size
	if (index >= vars.size()) {
		
		// At this point, all state and action variables are set
		// Just need to get the distribution for the appropriate CPT
		RDDL.EXPR e = cpf_expr.getDist(subs, _state);
//		System.out.println( _state + " " + e );
		double prob_true = -1d;
		//System.out.println("RDDL.EXPR: " + e);
		if (e instanceof KronDelta) {
			EXPR e2 = ((KronDelta)e)._exprIntValue;
			if (e2 instanceof INT_CONST_EXPR)
				// Should either be true (1) or false (0)... same as prob_true
				prob_true = (double)((INT_CONST_EXPR)e2)._nValue;
			else if (e2 instanceof BOOL_CONST_EXPR)
				prob_true = ((BOOL_CONST_EXPR)e2)._bValue ? 1d : 0d;
			else
				throw new EvalException("Unhandled KronDelta argument: " + e2.getClass()); 
		} else if (e instanceof Bernoulli) {
			prob_true = ((REAL_CONST_EXPR)((Bernoulli)e)._exprProb)._dValue;
		} else if (e instanceof DiracDelta) {
			// NOTE: this is not a probability, but rather an actual value
			//       (presumably for the reward).  This method is a little
			//       overloaded... need to consider whether there will be
			//       any problems arising from this overloading.  -Scott
			prob_true = ((REAL_CONST_EXPR)((DiracDelta)e)._exprRealValue)._dValue;
		} else
			throw new EvalException("Unhandled distribution type: " + e.getClass());
		
//		System.out.println(prob_true);
		
		// Now build CPT for action variables
		return _context.scalarMultiply(cpt, prob_true);

	} else {
//		System.out.println(vars + " " + index);
		
		PVAR_NAME p = (PVAR_NAME)vars.get(index)._o1;
		ArrayList<LCONST> terms = (ArrayList<LCONST>)vars.get(index)._o2;
		String var_name = CleanFluentName(p._sPVarName + terms + (p._bPrimed ? "\'" : "")); 
		//System.out.println(var_name);

		// Set to true
		_state.setPVariableAssign(p, terms, RDDL.BOOL_CONST_EXPR.TRUE);
		int high = _context.getVarNode(var_name, 0d, 1d);
		int ret_high = enumerateAssignments(vars, cpf_expr, subs, index + 1, _context.applyInt(cpt, high, ADD.ARITH_PROD));

		// Set to false
		_state.setPVariableAssign(p, terms, RDDL.BOOL_CONST_EXPR.FALSE);
		int low  = _context.getVarNode(var_name, 1d, 0d);
		int ret_low = enumerateAssignments(vars, cpf_expr, subs, index + 1, _context.applyInt(cpt, low, ADD.ARITH_PROD));
		
		// Unassign
		_state.setPVariableAssign(p, terms, null);
		
		int res = _context.applyInt(ret_high, ret_low, ADD.ARITH_SUM);
		
//		_context.getGraph(res).launchViewer();
//		System.in.read();
		// Sum both sides of the returned CPT and return it
		return res;
	}
}
	
	public static void main(String[] args) {
//		new ValueIterationConcurrent("/scratch/final_comp/rddl/elevators_mdp.rddl", 
//				"/scratch/final_comp/rddl/elevators_inst_mdp__1.rddl", 0.1, 
//				ValueIterationConcurrent.ORDER_GUESS, -1, "./");

		MaxDecomposition maxd = null;

		try{			
			BlockingQueue<Pair<ADD,Integer>> bq = new ArrayBlockingQueue<Pair<ADD,Integer>>(40);
			
			maxd = new MaxDecomposition(args[0], 
				args[1], 0.1, 
				MaxDecomposition.ORDER_GUESS, -1, "./", null);
			
			Thread t = new Thread(maxd, "max decomposition");
			t.start();
			t.join((long) (2*60*60*1e3d));
			System.out.println(maxd);
		}catch(Exception e){
		}
		
	}
	
	public String toString(){
		return rInfo.toString() +"\n"+ presolve.toString() +"\n"+ iData.toString() +"\n"+ postsolve.toString();
	}

}
